package com.ydlclass.config;

import com.ydlclass.IdGenerator;
import com.ydlclass.RegistryConfig;
import com.ydlclass.compress.Compress;
import com.ydlclass.compress.CompressFactory;
import com.ydlclass.loadbalancer.LoadBalance;
import com.ydlclass.serialize.Serialize;
import com.ydlclass.serialize.SerializerFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author duWenJian
 * @Date 2024/3/4 13:00
 * @Version 1.0
 */
@Slf4j
public class XmlResolver {
    /**
     * 读取xml文件中的配置信息
     * @param configuration
     */
    public void loadFromXml(Configuration configuration) {
        try {
            // 1、创建一个document
            InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream("yrpc.xml");
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 禁用DTD校验：可以通过调用setValidating(false)方法来禁用DTD校验。
            factory.setValidating(false);
            // 禁用外部实体解析：可以通过调用setFeature(String name, boolean value)方法并将“http://apache.org/xml/features/nonvalidating/load-external-dtd”设置为“false”来禁用外部实体解析。
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(inputStream);

            // 2、获取一个xpath解析器
            XPathFactory xPathFactory = XPathFactory.newInstance();
            XPath xPath = xPathFactory.newXPath();

            // 3、解析所有的标签
            configuration.setPort(resolvePort(doc,xPath));
            configuration.setAppName(resolveAppName(doc,xPath));
            configuration.setRegistryConfig(resolveRegistry(doc,xPath));
            configuration.setIdGenerator(resolveIdGenerator(doc,xPath));
            configuration.setSerializerType(resolveSerializeType(doc,xPath));
            configuration.setCompressionType(resolveCompressionType(doc,xPath));
            // 将xml加载的序列化与压缩方式 加载到对应的工厂
            ObjectWrapper<Compress> compressObjectWrapper = resolveCompress(doc, xPath);
            CompressFactory.addCompress(compressObjectWrapper);
            ObjectWrapper<Serialize> serializeObjectWrapper = resolveSerialize(doc, xPath);
            SerializerFactory.addSSerialize(serializeObjectWrapper);
            configuration.setLoadBalance(resolveLoadBalance(doc,xPath));

            // 如果有新增的标签,需要自己进行处理
        } catch(ParserConfigurationException | SAXException | IOException e){
            log.info("未发现相关配置文件或解析文件的时候发生异常，将选用默认配置");
        }
    }

    /**
     * 解析序列化协议对象
     * @param doc
     * @param xPath
     * @return ObjectWrapper<Serialize>
     */
    private ObjectWrapper<Serialize> resolveSerialize(Document doc, XPath xPath) {
        String expression = "/configuration/serializer";
        Byte code = Byte.valueOf(parseString(doc, xPath, expression, "code"));
        String type = parseString(doc, xPath, expression, "type");
        Serialize o = parseObject(doc, xPath, expression, null);
        return new ObjectWrapper<>(code,type,o);
    }

    /**
     * 解析压缩解压缩方式
     * @param doc
     * @param xPath
     * @return ObjectWrapper<Compress>
     */
    private ObjectWrapper<Compress> resolveCompress(Document doc, XPath xPath) {
        String expression = "/configuration/compression";
        Byte code = Byte.valueOf(parseString(doc, xPath, expression, "code"));
        String type = parseString(doc, xPath, expression, "type");
        Compress o = parseObject(doc, xPath, expression, null);
        return new ObjectWrapper<>(code,type,o);
    }

    /**
     * 解析负载均衡协议
     * @param doc
     * @param xPath
     * @return
     */
    private LoadBalance resolveLoadBalance(Document doc, XPath xPath) {
        String expression = "/configuration/loadBalance";
        //String parseString = parseString(doc, xPath, expression, "class");
        Object o1 = parseObject(doc, xPath, expression, null, null);
        LoadBalance loadBalance = (LoadBalance) o1;
        return loadBalance;
//        try {
//            Class<?> aClass = Class.forName(parseString);
//            Object o = aClass.getConstructor().newInstance();
//            return (LoadBalance) o;
//        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
//            throw new RuntimeException(e);
//        }
    }

    /**
     * 解析压缩协议
     * @param doc
     * @param xPath
     * @return
     */
    private String resolveCompressionType(Document doc, XPath xPath) {
        String expression = "/configuration/compressionType";
        String s = parseString(doc, xPath, expression,"type");
        return s;
    }

    /**
     * 解析序列化协议
     * @param doc
     * @param xPath
     * @return
     */
    private String resolveSerializeType(Document doc, XPath xPath) {
        String expression = "/configuration/serializerType";
        String parseString = parseString(doc, xPath, expression,"type");
        return parseString;
    }

    /**
     * 解析Port
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @return port
     */
    private int resolvePort(Document doc, XPath xPath) {
        String expression = "/configuration/port";
        return Integer.parseInt(parseString(doc, xPath, expression));
    }

    /**
     * 解析AppName
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @return appName
     */
    private String resolveAppName(Document doc, XPath xPath) {
        String expression = "/configuration/appName";
        return parseString(doc, xPath, expression);
    }

    /**
     * 解析注册中心
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @return Registry
     */
    private RegistryConfig resolveRegistry(Document doc, XPath xPath) {
        String expression = "/configuration/registry";
        String url = parseString(doc, xPath, expression, "url");
        return new RegistryConfig(url);
    }

    /**
     * 解析全局ID生成器
     * @param doc
     * @param xPath
     * @return
     */
    private IdGenerator resolveIdGenerator(Document doc, XPath xPath) {
        String expression = "/configuration/idGenerator";
        String clazz = parseString(doc, xPath, expression, "class");
        String dataCenterId = parseString(doc, xPath, expression, "dataCenterId");
        String machineId = parseString(doc, xPath, expression, "MachinId");

        try {
            Class<?> aClass = Class.forName(clazz);
            Object o = aClass.getConstructor(new Class[]{long.class, long.class})
                    .newInstance(Long.parseLong(dataCenterId), Long.parseLong(machineId));
            return (IdGenerator) o;
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 解析一个节点返回一个对象实例
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @param expression 表达式
     * @param paramsType 参数类型
     * @param param 参数
     * @param <T> 返回类型
     * @return 返回对象实例
     */
    private <T> T parseObject(Document doc, XPath xPath, String expression, Class<?> paramsType, Object... param) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮助我们获取节点
            Node targetNode = (Node)expr.evaluate(doc, XPathConstants.NODE);
            String ClassName = targetNode.getAttributes().getNamedItem("class").getNodeValue();
            Class<?> aClass = Class.forName(ClassName);
            Object instance = null;
            if (paramsType == null) {
                instance = aClass.getConstructor().newInstance();
            } else {
                instance = aClass.getConstructor(paramsType).newInstance(param);
            }
            return (T)instance;
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | XPathExpressionException e) {
            log.error("解析xml文件时发生异常，将选用默认配置",e);
        }
        return null;
    }


    /**
     * 读取xml文件返回一个字符串,获得一个节点的文本属性
     * @param doc 文档对象
     * @param xPath xpath解析器
     * @param expression 表达式
     * @param AttributeName 属性名
     * @return
     */
    private String parseString(Document doc, XPath xPath, String expression, String AttributeName) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮助我们获取节点
            Node targetNode = (Node)expr.evaluate(doc, XPathConstants.NODE);
            String ClassName = targetNode.getAttributes().getNamedItem(AttributeName).getNodeValue();
            return ClassName;
        } catch (XPathExpressionException e) {
            log.error("解析xml文件时发生异常，将选用默认配置",e);
        }
        return null;
    }

    /**
     * 获得一个节点的文本
     * @param doc
     * @param xPath
     * @param expression
     * @return
     */
    private String parseString(Document doc, XPath xPath, String expression) {
        try {
            XPathExpression expr = xPath.compile(expression);
            // 我们的表达式可以帮助我们获取节点
            Node targetNode = (Node)expr.evaluate(doc, XPathConstants.NODE);
            return targetNode.getTextContent();
        } catch (XPathExpressionException e) {
            log.error("解析xml文件时发生异常，将选用默认配置",e);
        }
        return null;
    }

    public static void main(String[] args) {
        Configuration configuration = new Configuration();
    }

}
